package org.zoomdy.gdbserver.extender;

import java.io.*;

public class Packet
{
    public Packet(byte[] rawData)
    {
        this._raw = rawData;
        this._type = Type.UNKNOWN;
        
        if(this._raw.length >= 4)
        {
            if((this._raw[0] == '$') && (this._raw[this._raw.length-3] == '#'))
            {
                this._isNormal = true;
                this._type = Type.NORMAL;
            }
            else
            {
                // TODO 错误的包，抛出异常
            }
        }
        else if(this._raw.length == 1)
        {
            if(this._raw[0] == '+')
            {
                this._isAck = true;
                this._type = Type.ACK;
            }
            else if(this._raw[0] == '-')
            {
                this._isNack = true;
                this._type = Type.NACK;
            }
            else if(this._raw[0] == CTRL_C)
            {
                this._isCtrlC = true;
                this._type = Type.CTRL_C;
            }
            else
            {
                // TODO 错误的包，抛出异常
            }
        }
        else
        {
            // TODO 长度错误，抛出异常
        }
    }
    
    public byte[] getRaw()
    {
        return this._raw;
    }
    
    public String toRawString()
    {
        if(this._rawStr == null)
        {
            if(this._isNormal)
            {
                this._rawStr = new String(this._raw);
            }
            else if(this._isCtrlC)
            {
                this._rawStr = "***Ctrl+C***";
            }
            else
            {
                this._rawStr = new String(this._raw);
            }
        }
        
        return this._rawStr;
    }
    
    public String toString()
    {
        if(this._str == null)
        {
            if(this._isNormal)
            {
                this._str = new String(this._raw, 1, this._raw.length - 4);
            }
            else if(this._isCtrlC)
            {
                this._str = "***Ctrl+C***";
            }
            else
            {
                this._str = new String(this._raw);
            }
        }
        
        return this._str;
    }
    
    public boolean isNormal()
    {
        return this._isNormal;
    }
    
    public boolean isAck()
    {
        return this._isAck;
    }
    
    public boolean isNack()
    {
        return this._isNack;
    }
    
    public boolean isCtrlC()
    {
        return this._isCtrlC;
    }
    
    public Type getType()
    {
        return this._type;
    }
    
    public enum Type
    {
        UNKNOWN, NORMAL, ACK, NACK, CTRL_C;
    }
    
    public boolean startsWith(String s)
    {
        if(!_isNormal)
        {
            return false;
        }
        
        byte[] b = s.getBytes();
        
        for(int i = 0; i < b.length; i++)
        {
            if(i + 1 >= _raw.length)
            {
                return false;
            }
            
            // 比较时忽略包起始符号"$"
            if(_raw[i + 1] != b[i])
            {
                return false;
            }
        }
        
        return true;
    }
    
    public String parsePacketAsString() throws IOException
    {
        String str = this.toString();
        if(str.equals("E01"))
        {
            throw new IOException("E01");
        }
        
        byte[] bytes = new byte[str.length() / 2];
        int count = 0;
        
        for(int i = 0; i < str.length() / 2; i++)
        {
            String subStr = str.substring(i * 2, i * 2 + 2);
            int b = Integer.parseInt(subStr, 16);
            if(b == 0)
            {
                break;
            }
            bytes[i] = (byte)b;
            count++;
        }
        
        return new String(bytes, 0, count);
    }
    
    public int parsePacketAsHalfWord() throws IOException
    {
        String str = this.toString();
        if(str.equals("E01"))
        {
            throw new IOException("E01");
        }
        
        int halfWord = Integer.parseInt(str, 16); // 转换字符串到数字，16进制，字节次序是颠倒的
        halfWord = (((halfWord >> 0) & 0xff) << 8) // 字节次序交换
                 | (((halfWord >> 8) & 0xff) << 0);
        
        return halfWord & 0xffff;
    }
    
    public long parsePacketAsWord() throws IOException
    {
        String str = this.toString();
        if(str.equals("E01"))
        {
            throw new IOException("E01");
        }
        
        long word = Long.parseLong(str, 16); // 转换字符串到数字，16进制，字节次序是颠倒的
        word = (((word >> 0) & 0xff) << 24) // 字节次序交换
             | (((word >> 8) & 0xff) << 16) 
             | (((word >> 16) & 0xff) << 8) 
             | (((word >> 24) & 0xff) << 0);
        
        return word & 0xffffffff;
    }
    
    static public Packet makeMemReadPacket(long addr, int len)
    {
        String str = "m";
        str += Long.toHexString(addr) + ",";
        str += Integer.toHexString(len);
        
        return makeNormalPacket(str);
    }
    
    static public Packet makeNormalPacket(String str)
    {
        int sum = 0;
        byte[] strByte = str.getBytes();
        byte[] raw = new byte[strByte.length + 4];
        
        for(int i = 0; i < strByte.length; i++)
        {
            raw[i + 1] = strByte[i];
            sum += strByte[i];
        }
        
        sum = sum & 0xff;
        String sumHex = Integer.toHexString(sum);
        byte[] sumByte = sumHex.getBytes();
        
        raw[0] = '$';
        raw[strByte.length + 1] = '#';

        if(sumByte.length > 1)
        {
            raw[strByte.length + 2] = sumByte[0];
            raw[strByte.length + 3] = sumByte[1];
        }
        else
        {
            raw[strByte.length + 2] = '0';
            raw[strByte.length + 3] = sumByte[0];
        }
        
        Packet pkt = new Packet(raw);
        
        return pkt;
    }
    
    static public Packet makeAckPacket()
    {
        byte[] raw = new byte[1];
        
        raw[0] = '+';
        
        Packet pkt = new Packet(raw);
        
        return pkt;
    }
    
    static public Packet makeNackPacket()
    {
        byte[] raw = new byte[1];
        
        raw[0] = '-';
        
        Packet pkt = new Packet(raw);
        
        return pkt;
    }
    
    static public Packet makeCtrlCPacket()
    {
        byte[] raw = new byte[1];
        
        raw[0] = CTRL_C;
        
        Packet pkt = new Packet(raw);
        
        return pkt;
    }
    
    final private byte[] _raw;
    private String _str;
    private String _rawStr;
    private boolean _isNormal;
    private boolean _isCtrlC;
    private boolean _isAck;
    private boolean _isNack;
    private Type _type;
    
    static final public byte CTRL_C = 0x03;
}
